# encoding: utf-8

from cElementTree import fromstring, tostring, Element

from gcapi._root import _Root
from datetime import datetime, date, timedelta
import re

class Who(_Root):
    
    def __init__(self, name, email, type, state):
        self._name = name
        self._email = email
        self._type = type
        self._state = state    # zugesagt (accepted), invited (eingeladen) 

    def getName(self):
        return self._name
    
    name = property(getName)

    def getEmail(self):
        return self._email
    
    email = property(getEmail)

    def getType(self):
        return self._type
    
    type = property(getType)
    
    def getState(self):
        return self._state
    
    state = property(getState)
    
class Entry(_Root):
    
    _DATETIME_RE = "(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)(T(\\d\\d):(\\d\\d):(\\d\\d)(\.\\d+[1-9])?(Z|((\+|-)(\\d\\d):(\\d\\d)))?)?"
    
    def __init__(self):
        self._id = None
        self._edit_uri = None
        self._title = None
        self._published = None
        self._updated = None
        self._content = None
        self._where = None
        self._who = []
        self._startTime = None
        self._endTime = None
        self._reminder = None
        self._notify = False
        self._datetime_re = re.compile(self._DATETIME_RE)
    
    @classmethod
    def createFromText(cls, text): #@UnusedVariable
        entries = []
        
        tree = fromstring(text)
        nodeText = "{%s}entry" % (Entry._ATOM_SCHEMA_URI,)
        
        nodes = tree.findall(nodeText)
        
        for node in nodes:
            entry = Entry()
            entry.applyId(node)
            entry.applyTitle(node)
            entry.applyPublished(node)
            entry.applyUpdated(node)
            entry.applyContent(node)
            entry.applyWhere(node)
            entry.applyWho(node)
            entry.applyWhen(node)
            entry.applyNotify(node)
            entry.applyLinks(node)
        
            entries.append(entry)
        return entries
    
    def updateFromText(self, text):
        tree = fromstring(text)
        
        if tree:
            self.applyId(tree)
            self.applyPublished(tree)
            self.applyUpdated(tree)
            self.applyLinks(tree)
    
    def asXML(self):
        rootTag = "{%s}entry" % (Entry._ATOM_SCHEMA_URI,)
        rootNode = Element(rootTag)

        # cateory tag ist für die calendar notwendig
        categoryTag = "{%s}category" % (Entry._ATOM_SCHEMA_URI,)
        categoryNode = Element(categoryTag, )
        categoryNode.attrib["schema"] = "http://schemas.google.com/g/2005#kind"
        categoryNode.attrib["term"] = "http://schemas.google.com/g/2005#event"
        rootNode.append(categoryNode)
        
        # id
        if self.id:
            idTag = "{%s}id" % (Entry._ATOM_SCHEMA_URI,)
            idNode = Element(idTag)
            idNode.text = self.id
            rootNode.append(idNode)
        
        # title
        if self.title:
            titleTag = "{%s}title" % (Entry._ATOM_SCHEMA_URI,)
            titleNode = Element(titleTag)
            titleNode.attrib["type"] = "text"
            titleNode.text = self.title
            rootNode.append(titleNode)
        
        # published
        if self.published: # hier sollte glaube ich nicht geschehen
            pass
        
        # content
        if self.content:
            contentTag = "{%s}content" % (Entry._ATOM_SCHEMA_URI,)
            contentNode = Element(contentTag)
            contentNode.attrib["type"] = "text"
            contentNode.text = self.content
            rootNode.append(contentNode)
  
        # where
        if self.where:
            whereTag = "{%s}where" % (Entry._GOOGLE_SCHEMA_URI,)
            whereNode = Element(whereTag)
            whereNode.attrib["valueString"] = self.where
            rootNode.append(whereNode)
        
        # who
        if self.who:
            whoTag = "{%s}who" % (Entry._GOOGLE_SCHEMA_URI,)
            whoStateTag = "{%s}attendeeStatus" % (Entry._GOOGLE_SCHEMA_URI,)
            for who in self.who:
                whoNode = Element(whoTag)
                whoNode.attrib["rel"] = who.type
                whoNode.attrib["valueString"] = who.name
                whoNode.attrib["email"] = who.email
                whoStateNode = Element(whoStateTag)
                whoStateNode.attrib["value"] = who.state
                whoNode.append(whoStateNode)
                rootNode.append(whoNode) 
        
        # when 
        if self.startTime and self.endTime:
            whenTag = "{%s}when" % (Entry._GOOGLE_SCHEMA_URI)
            whenNode = Element(whenTag)
            whenNode.attrib["startTime"] = str(self.startTime)
            whenNode.attrib["endTime"] = str(self.endTime)
            
            if self.reminder:
                reminderTag = "{%s}reminder" % (Entry._GOOGLE_SCHEMA_URI,)
                reminderNode = Element(reminderTag)
                reminderNode.attrib["minutes"] = self.reminder
                whenNode.append(reminderNode)
                
            rootNode.append(whenNode)
        
        return tostring(rootNode)
    
    def getId(self):
        return self._id
    
    def setId(self, id):
        self._id = id
        
    id = property(getId, setId)
    
    def getEditURI(self):
        return self._edit_uri
    
    editURI = property(getEditURI)
    
    def getTitle(self):
        return self._title
    
    def setTitle(self, title):
        self._title = title
        
    title = property(getTitle, setTitle)
    
    def getPublished(self):
        return self._published
    
    def setPublished(self, published):
        self._published = published
        
    published = property(getPublished, setPublished)
    
    def getUpdated(self):
        return self._updated
    
    def setUpdated(self, updated):
        self._updated = updated
        
    updated = property(getUpdated, setUpdated)

    def getContent(self):
        return self._content
    
    def setContent(self, content):
        self._content = content
        
    content = property(getContent, setContent)
    
    def getWhere(self):
        return self._where
    
    def setWhere(self, where):
        self._where = where
        
    where = property(getWhere, setWhere)
    
    def getWho(self):
        return self._who
    
    who = property(getWho)
    
    def getStartTime(self):
        return self._startTime
    
    def setStartTime(self, startTime):
        self._startTime = startTime
        
    startTime = property(getStartTime, setStartTime)
    
    def getEndTime(self):
        return self._endTime
    
    def setEndTime(self, endTime):
        self._endTime = endTime
        
    endTime = property(getEndTime, setEndTime)
    
    def getReminder(self):
        return self._reminder
    
    def setReminder(self, reminder):
        self._reminder = reminder
        
    reminder = property(getReminder, setReminder)
    
    def getNotify(self):
        return self._notify
    
    def setNotify(self, notify):
        self._notify = notify
        
    notify = property(getNotify, setNotify)
    
    def applyId(self, node):
        nodeText = "{%s}id" % (Entry._ATOM_SCHEMA_URI,)
        self._id = node.findall(nodeText)[0].text
    
    def applyTitle(self, node):
        nodeText = "{%s}title" % (Entry._ATOM_SCHEMA_URI,)
        self._title = node.findall(nodeText)[0].text
    
    def applyPublished(self, node):
        nodeText = "{%s}published" % (Entry._ATOM_SCHEMA_URI,)
        self._published = node.findall(nodeText)[0].text
    
    def applyUpdated(self, node):
        nodeText = "{%s}updated" % (Entry._ATOM_SCHEMA_URI,)
        self._updated = node.findall(nodeText)[0].text
    
    def applyContent(self, node):
        nodeText = "{%s}content" % (Entry._ATOM_SCHEMA_URI,)
        self._content = node.findall(nodeText)[0].text
    
    def applyWhere(self, node):
        nodeText = "{%s}where" % (Entry._GOOGLE_SCHEMA_URI,)
        self._where = node.findall(nodeText)[0].get("valueString")
    
    def applyWho(self, node):
        nodeText = "{%s}who" % (Entry._GOOGLE_SCHEMA_URI,)
        
        for item in node.findall(nodeText):
            email = item.get("email")
            name = item.get("valueString")
            type = item.get("rel")
            stateNodeText = "{%s}attendeeStatus" % (Entry._GOOGLE_SCHEMA_URI,)
            state = item.findall(stateNodeText)[0].get("value")
            
            currentWho = Who(name, email, type, state)
            self._who.append(currentWho)
    
    def applyWhen(self, node):
        nodeText = "{%s}when" % (Entry._GOOGLE_SCHEMA_URI,)
        whenNodes = node.findall(nodeText)
        
        if whenNodes:
            self._startTime = self._parseXsDateTime(whenNodes[0].get("startTime"))
            self._endTime = self._parseXsDateTime(whenNodes[0].get("endTime"))
            
            # startTime and endTime should be either both dates or datetimes
            # according to the GCal API.
            # We assume startTime to be of the correct type and convert endTime
            # to the same type
            if type(self._startTime) == date and type(self._endTime) == datetime:
                self._endTime = date(self._endTime.year, 
                                    self._endTime.month, 
                                    self._endTime.day)
            elif type(self._startTime) == datetime and type(self._endTime) == date:
                self._endTime = datetime(self._endTime.year, 
                                        self._endTime.month, 
                                        self._endTime.day)

            self.applyReminder(whenNodes[0])
 
    def applyReminder(self, node):
        nodeText = "{%s}reminder" % (Entry._GOOGLE_SCHEMA_URI,)
        reminderNodes = node.findall(nodeText)
        
        if reminderNodes:
            self._reminder = reminderNodes[0].get("minutes")
    
    def applyNotify(self, node):
        nodeTag = "{%s}sendEventNotifications" % (Entry._GOOGLE_CALENDAR_SCHEMA_URI,)
        notifyNode = node.findall(nodeTag)
        
        if notifyNode:
            if notifyNode[0].get("value") == 'true':
                self.notify = True
            else:
                self.notify = False

    def applyLinks(self, node):
        nodeTag = "{%s}link" % (Entry._ATOM_SCHEMA_URI,)
        linkNodes = node.findall(nodeTag)
        
        for linkNode in linkNodes:
            if linkNode.get("rel") == 'edit':
                self._edit_uri = linkNode.get("href")
                
    def _parseXsDateTime(self, dateTimeStr):
        """
        Parses a string in xs:dateTime format and returns a Python object that
        represents that dateTime. The returned object is either a datetime.date
        or datetime.datetime object, depending on whether the given xs:dateTime
        contains time information or not.
        
        If the given xs:dateTime contains timezone information, that information
        is used to convert the time to UTC (Coordinated Universal Time).
        If timezone information is absent the time is assumed to be in UTC.
        """
        # object to be returned
        dateTime = None
        
        if dateTimeStr == None or dateTimeStr.strip() == "":
            return None
        
        match = self._datetime_re.match(dateTimeStr)
        
        # invalid format
        if not match:
            raise ValueError("Invalid date format")
        
        groups = match.groups()

        year        = groups[0] and int(groups[0]) or 0
        month       = groups[1] and int(groups[1]) or 0
        day         = groups[2] and int(groups[2]) or 0
        hours       = groups[4] and int(groups[4]) or 0
        minutes     = groups[5] and int(groups[5]) or 0
        seconds     = groups[6] and int(groups[6]) or 0
        microSeconds= groups[7] and int(groups[7][1:]) or  0
        tzSign      = groups[10] and groups[10]
        tzHours     = groups[11] and int(groups[11]) or 0
        tzMinutes   = groups[12] and int(groups[12]) or 0
        
        # if there is time information
        if hours:
            dateTime = datetime(
                    year,
                    month,
                    day,
                    hours,
                    minutes,
                    seconds,
                    microSeconds)

            # handle the timezone 
            if tzSign:
                hour_delta = int("%s%s" % (tzSign, tzHours))
                minute_delta = int("%s%s" % (tzSign, tzMinutes))  
                dateTime = dateTime - timedelta(hours=hour_delta, minutes=minute_delta)
                
        # otherwise it's just a date
        else:
            dateTime = date(year, month, day)
        return dateTime
        
    def __str__(self):
        if self.title:
            return "%s: '%s'" % (str(self.__class__), self.title)
        else:
            return str(self.__class__)
    __repr__ = __str__